#include <string.h>
#include <cstdlib>
#include <iostream>
#include "Set.cpp"
#include "Human.h"
#include <vector>
#include "Meld.h"
#include <stdexcept> 
#include <limits>
#include <cmath>

using std::ostream;
using std::cin;
using std::cout;
using std::endl;
using std::flush;
using std::vector;

#define MAX_CARD_INPUT 50
#define GO -2
#define ERROR -1

//gets option
int getOption(int min, int max)
{
	int option;
	cout << "\nHuman! Enter your choice now: ";
	cin >> option;
	
	while (!cin || !(option >= min && option <= max)) 
	{
			cout << "Invalid option, please try again." << endl;
			cin.clear();
			cin.ignore(std::numeric_limits<int>::max(), '\n');
			cin >> option;
	}
	//cin.ignore(std::numeric_limits<int>::max(), '\n');
	return option;

/*

int option;
	int option2;
	cout << "\nInput choice: ";
	cin >> option;
	
	while (!(option >= min && option <= max))
	{
		//cin.clear();
		//cin >> option;
		//cout << "option" << option << endl;
		cout << "Invalid option. New Try: " << endl;
		cin >> option2;
		cout << "option2" << option2 << endl;
	}
	return option2; */
}

//prints main menu and gets option
int getMainOption()
{
	int mainOption = -1; 
	cout << endl;
	cout << "Puny Human's Options:" << endl;
	cout << "1. Draw a card and end turn at beginning of turn" << endl;
	cout << "2. Create a new meld from hand" << endl;
	cout << "3. Add to an existing meld from hand" << endl;
	cout << "4. Split an existing meld on the table" << endl;
	cout << "5. Join existing melds on the table" << endl;
	cout << "6. End turn" << endl;
	cout << "7. Restart" << endl;
	
	mainOption = getOption(0,7);
	//cout << "option chosen: " << mainOption << endl;
	return mainOption;
}


vector<string> splitSpace(string s)
{
  std::vector<string> ret;
	string temp;
	bool isSpace = false;
  for (unsigned int i = 0; i < s.size(); i++)
  {
		if ( isspace(s[i]) ) 
		{
			if (!isSpace)
			{
				ret.push_back(temp);
				temp.clear();
			}
			isSpace = true;	
		}
		else 
		{
		  temp += s[i];		
			isSpace = false;
 		}	
  }
	if (!isSpace)
		ret.push_back(temp);
	return ret;
}

bool validSuit(char s)
{
	s = toupper(s);
	return (s == 'H' || s == 'S' || s == 'D' || s == 'C' );
}

/**
 * @returns True if the string card is a valid card representation and the card is in the player's hand.
 */
bool validateCard(string card)
{	
	string cVal;
	if (card.size() < 2) //min one number and one letter
		return false;


	//check face value	
	unsigned int a = 0;
	for(; card.size() > a && isdigit(card[a]); a++)
		cVal += card[a];
	int cValue = atoi(cVal.c_str());
	if (!(cValue > 0 && cValue < 15))
		return false;

	//check suit
	if (card.size() == a || !validSuit(card[a]))
		return false;
	
	return true;
}

/*
 * Validates an input card string.  For example...
 * 
 * @param An input string of card values.
 * @return True if the input string was a valid string of card values
 */
bool validateCardList(string cardstring)
{
	bool valid = true;
  std::vector<string> cardlist = splitSpace(cardstring);
	for (std::vector<string>::iterator i = cardlist.begin();
			i != cardlist.end(); i++)
	{
		if (!validateCard(*i)) valid = false;
	}
	return valid;
}
/*
 * Asks user for a string of cards until a valid card string is entered.
 * @return A validated card string.
 */
string getCards()
{
	//Input buffer to read line into
	char in[MAX_CARD_INPUT];
	char temp[MAX_CARD_INPUT];
	//string that holds the input buffer
	string inCards;		
	
		inCards = "";	
		cout << "Insert cards separated by a space (for example: 3D 4D 5D): " << endl;
		cin.getline(temp, MAX_CARD_INPUT); 		
		cin.getline(in, MAX_CARD_INPUT);	
		inCards = in;
		cout << "Read in: " << inCards << endl;
	if (!validateCardList(inCards))  //repeat loop until inCards is valid
		throw std::invalid_argument("Invalid card input");
 	return inCards;
}

Set<Card>& Human::cardStringToSet(string s, Set<Card>& set)
{
	std::vector<string> cardlist = splitSpace(s);
	for (std::vector<string>::iterator i = cardlist.begin();
			i != cardlist.end(); i++)
	{
		set.add(Card(*i));
	}
	return set;
}


Human::Human(string str):Player(str, "Human"){}

ostream& Human::display(ostream& os) const
{
	os << "Type: " << Player::type;
	os << "\tName: " << Player::name << flush;
	return os;
}

bool Human::areCardsInHand(Set<Card> input, vector<Card> hand)
{
	bool found = false;
	Card currCard; //inputcard

	//iterate through set of cards
	SetIter<Card> inputIter(input); //iterate through user input
	while (inputIter.hasNext())
	{
		currCard = inputIter.next();
		//cout << "CUrrent card to be check for! " << currCard << "\n" <<endl;
		found = false;    
		
		//iterate through hand to search for card
		for (int w = 0; w < hand.size(); w++)
//vector<Card>::iterator i = hand.begin(); i < hand.end(); i++)
		{	
			Card thisCard = hand[w];
			//cout << "card in hand: "<< hand[w] <<endl;
			if (currCard == thisCard)
				found = true;
		}
		if (!found)
		{	
			cout << "Card not found: " << currCard << endl; 	
			return false;
		}			
			
	}
	return true;
}


int getMeldIndex(vector<Meld*>& table) 
{
  int thisIndex;

  do{
  	cout<<"Choose Meld by index to modify: ";
  	cin>>thisIndex;
  	if(thisIndex>table.size())
    	cout<<"ERROR: You must input an integer between 1 and " << table.size()+1 << endl;
  }
  while(!(thisIndex > 0 && thisIndex < table.size()+1)); 
  
  return thisIndex-1; 
}

bool Human::move(vector<Meld*>& table)
{
  handBuffer = hand;
  cout<< "\n\nThe illustrious " << name<< ", make your move." << endl;
  vector<Meld*> tableBackup;

 vector<Meld *>::iterator tableIterator = tableBackup.begin();
 while(tableIterator!=tableBackup.end()){
   delete *tableIterator; 
   tableBackup.erase(tableIterator);
 }
 
 
 vector<Meld *>::iterator tableTerator = table.begin();
 while(tableTerator!=table.end()){
   //   cout<<"THIS ITERATORPOINT: "<<**tableTerator<<endl;
   if ( (**tableTerator).validify() == -1)  tableBackup.push_back( new Group( (**tableTerator).getCardSet()));
   if ( (**tableTerator).validify() == 1)  tableBackup.push_back(new Run( (**tableTerator).getCardSet()) );
   tableTerator++; 
 }
 
  vector<Card> handBufferBackup(handBuffer);

  int mainOp = GO;
  bool firstMove = true;
  string inCards;
  Set<Card> cardSet;
	Card thisCard;
	Meld  * currMeld;
	Meld  * currMeld2;
	Meld * meldCheck;
	int index;
	int meld2Index;
	int meldIndex;
  
  while (mainOp == ERROR || mainOp == GO)
  {
	 	cout<<"\n\tYour Hand: " << endl << "\t";
	 	displayHandBuffer(cout);
		cout<<endl;
	 	cout<< "\tGame table: " << endl;
	 	displayTableBuffer(cout,table);
		cout<<endl;
	  
		mainOp = getMainOption();
		inCards.clear();
		cardSet.clear();
	//	currMeld = & Meld();
	//	currMeld2 =  Meld();
		
		try{
   
	  switch (mainOp)
	  {
	      cout << "Option for switch menu: " << mainOp;
	    case 1: //draw a card and end turn
	      if (firstMove)
				{
					//cout << "Fawaz ---draw a card." << endl;
					return false;	
				}
				else
				mainOp = ERROR;
				break;
			case 2: //create a meld
				inCards = getCards();
					
				cardSet = cardStringToSet(inCards, cardSet);	
				if (areCardsInHand(cardSet, this->handBuffer))
				{		
					SetIter<Card> thisIter(cardSet); 
					while(thisIter.hasNext()){
						thisCard = thisIter.next();
				//		cout<<"THE CARD: "<<thisCard<<endl;
						removeCardFromHand(thisCard,handBuffer);
					}
					meldCheck = new Meld(cardSet);
					table.push_back(new Meld(cardSet));
					if (meldCheck->validify() == 1)
						firstMoveDone = true;
					mainOp = GO;
					firstMove = false;
				}	
				else 
					mainOp = ERROR;
		//		cout << "main op in 2: " << mainOp << endl;
				break;
			case 3: //add to an existing meld from handBuffer
				inCards = getCards();
				cardSet = cardStringToSet(inCards, cardSet);
				if (areCardsInHand(cardSet, this->handBuffer))
				{

					SetIter<Card> thisIter(cardSet); 
					while(thisIter.hasNext()){
						thisCard = thisIter.next();
						cout<<"THE CARD: "<<thisCard<<endl;
						removeCardFromHand(thisCard,handBuffer);
					}						
					table[getMeldIndex(table)]->add(cardSet);
					mainOp = GO;
					firstMove = false;
				}
				else
					mainOp = ERROR;
				break;
			case 4: //split an existing meld from the table
				currMeld = table[getMeldIndex(table)]; //getMeld from user
				(*currMeld).printIndexes(cout) << endl; //display meld
				do{
					cout << "Tell me where do you want to split this meld: ";
					cin >> index;
				}while (index < 0 || index > currMeld->size());
				table.push_back(currMeld->split(index-1));
				mainOp = GO;
				firstMove = false;
				break;
	    case 5: {//join existing melds on the table
				cout << "First meld--";
				meldIndex = getMeldIndex(table);
				currMeld = table[meldIndex];
				cout << "Second meld--";
				meld2Index = getMeldIndex(table);
				if (meldIndex == meld2Index)
				{
					mainOp = ERROR;
					break;
				}
				currMeld2 = table[meld2Index];
				currMeld->add(*currMeld2);
				vector<Meld*>::iterator someIter = table.begin();
				table.erase(someIter+meld2Index);
				firstMove = false;
				mainOp = GO;
				break;
	    }
			case 6: //end turn
				if (!firstMove)
					return true; 
				else mainOp = ERROR;
				break;
			case 7: //restart 
			  {vector<Meld *>::iterator tableIterator = table.begin();
				 while(tableIterator!=table.end()){
				   delete *tableIterator; 
				   table.erase(tableIterator);
				 }
				 
				 
				 vector<Meld *>::iterator tableTerator = tableBackup.begin();
				 while(tableTerator!=tableBackup.end()){
				   //   cout<<"THIS ITERATORPOINT: "<<**tableTerator<<endl;
				   if ( (**tableTerator).validify() == -1)  table.push_back( new Group( (**tableTerator).getCardSet()));
				   if ( (**tableTerator).validify() == 1)  table.push_back(new Run( (**tableTerator).getCardSet()) );
				   tableTerator++;
				 }
				 
				handBuffer = handBufferBackup;
				firstMove = true;
				mainOp = GO;
				break;
	  }
			default:
				cout << "Oops. This shoudn't happen." << endl;
				break;
			
		}//end switch statement
		} //end try
		catch (std::invalid_argument& invalid)	
		{
			cout << "Exception: " << invalid.what() << endl;
			mainOp = ERROR;
		}
		catch (std::out_of_range& range)
		{
			cout << "Exception: Out of Range\n" << endl;
			mainOp = ERROR;
		}		
		catch (...)
		{
			cout << "WHAT OTHER EXCEPTIONS ARE THERE???\n\n" << endl;
			mainOp = ERROR;
		}
		if (mainOp == ERROR)
			cout << "Try again.\n" << endl;

	}
  return false;
  //return
}


void Human::removeCardFromHand(Card thisCard, vector<Card>& handBuffer) 
{
  	bool hasErased = false;
		for(vector<Card>::iterator handTerator = handBuffer.begin();handTerator < handBuffer.end();handTerator++) 
			if (thisCard == *handTerator && !hasErased)
			{
			//	cout<<"CARD TO BE ITERATED OVER: "<<*handTerator<<endl;
				handBuffer.erase(handTerator);
				hasErased = true;
			}
}
